/*
    调度器：根据消息的类型调用不同的业务回调函数，类似于根据消息的类型进行消息分发
        1、提供注册业务回调函数的接口，由业务层来注册
        2、提供消息回调（调度接口），设置给MuduoServer/MuduoClient，当下层接收到消息的时候，此函数能够根据不同的消息类型找到对应业务回调函数，并调用

    调度器设计原因：
        1、服务端和客户端收到消息之后都需要根据消息类型来调用不同的回调函数，如果直接在MuduoServer/MuduoClient的onMessage函数中通过if语句等方式来实现，有以下缺点：
           a.代码就要写两份，容易代码冗余
           b.根据开闭原则，这样后期代码的扩展性不好，如果需要增加一个新的业务回调，就需要修改源码
        2、从底层的网络模块将该功能抽离出来，设计调度器模块。如果后期要新增业务回调，只需要通过注册业务回调函数的接口注册就行

    调度器设计过程：
        1、如果业务层注册的业务回调函数类型是：void(const ns_jsonrpc::BaseConnection::BaseConnectionPtr &conn, ns_jsonrpc::BaseMessage::BaseMessagePtr &msg)
           那么业务层就无法直接通过基类消息对象中的方法获取body中特有的字段，因为基类中并没有提供这样的方法，而是由不同消息的派生类各自实现获取body中字段的方法。
           此时就需要业务层自己进行向下类型转换，才能正确调用获取body中字段的方法。但是这样的话，业务层回调函数的复杂度就变高了
           所以这个业务回调函数的第二个参数应该是具体的消息类型，当下层接收到基类消息的时候我统一在Dispatcher中来完成向下转换
        2、如果是具体消息类型，那么消息种类很多，Dispatcher::registerCallback()接口第二个参数就应该是不确定的，所以此函数应该是模板函数
           T应该表示函数类型，但是此时又出现问题，_callback_map表中的value不能是不同的类型；那么对回调函数封装为一个Callback类，但是此类也是模板类，无法在_callback_map中使用
           根据多态，父类指针可以指向子类对象，那么让value成为Callback类的父类指针，这样就可以了；所以Callback需要继承一个父类BaseCallback，此时问题看似好像都解决了
        3、实现Dispatcher::onMessage函数又出现了问题？当通过映射表找到业务回调函数的时候无法直接调用，因为这个函数的类型是一个模板类型，无法确定参数的类型，所以无法传参
           同时更无法将基类消息转换为具体的类型消息传入到此函数，因为我也不知道具体的消息类型是什么。所以上面的T设计的有问题。T应该代表消息类型，然后重新定义业务回调类型
           using TypeMessageCallback = std::function<void(const BaseConnection::BaseConnectionPtr &, std::shared_ptr<T> &)>
*/

#pragma once
#include <unordered_map>
#include "net.hpp"

namespace ns_jsonrpc
{
    class BaseCallback
    {
    public:
        using BaseCallbackPtr = std::shared_ptr<BaseCallback>;

    public:
        virtual void onMessage(const BaseConnection::BaseConnectionPtr &conn, BaseMessage::BaseMessagePtr &msg) = 0;
    };

    // 注意：类模板中T是消息的类型，不是函数的类型
    template <typename T>
    class Callback : public BaseCallback
    {
    public:
        using CallbackPtr = std::shared_ptr<Callback<T>>;
        using TypeMessageCallback = std::function<void(const BaseConnection::BaseConnectionPtr &, std::shared_ptr<T> &)>;

    public:
        Callback(const TypeMessageCallback &callback) : _callback(callback)
        {
        }

        void onMessage(const BaseConnection::BaseConnectionPtr &conn, BaseMessage::BaseMessagePtr &msg) override
        {
            // 此时_callback的第二个参数（消息）是具有类型的，所以需要类型转换
            auto typeMsg = std::dynamic_pointer_cast<T>(msg);
            _callback(conn, typeMsg);
        }

    private:
        TypeMessageCallback _callback;
    };

    class CallbackFactory
    {
    public:
        template <typename T>
        static std::shared_ptr<Callback<T>> create(const typename Callback<T>::TypeMessageCallback &callback)
        {
            return std::make_shared<Callback<T>>(callback);
        }
    };

    class Dispatcher
    {
    public:
        using DispatcherPtr = std::shared_ptr<Dispatcher>;

    public:
        // 注册业务回调函数的接口，回调函数由业务层进行提供，用来处理不同消息类型的业务
        template <typename T>
        void registerCallback(MType mtype, const typename Callback<T>::TypeMessageCallback &cb)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            BaseCallback::BaseCallbackPtr callback = CallbackFactory::create<T>(cb);
            _callback_map[mtype] = callback;
        }

        // 消息回调（调度函数），设置给MuduoServer/MuduoClient。当下层接收到消息的时候回调此函数，此函数能够根据消息类型找到对应的业务回调函数，并调用
        void onMessage(const BaseConnection::BaseConnectionPtr &conn, BaseMessage::BaseMessagePtr &msg)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _callback_map.find(msg->mType());
            if (it == _callback_map.end())
            {
                LOG_ERROR("message type is illegal, no callback function for message type: %d\n", msg->mType());
                conn->shutdown();
                return;
            }
            it->second->onMessage(conn, msg);
        }

    private:
        std::unordered_map<MType, BaseCallback::BaseCallbackPtr> _callback_map; // 保存消息类型和业务回调函数的表
        std::mutex _mutex;
    };
}